#include <jni.h>
#include <string>

#include "Core/Assembler.h"
#include "Core/Misc.h"

/**
 * String com.king.armips.ArmipsDroid#stringFromJNI()
 */
extern "C" JNIEXPORT jstring JNICALL
Java_com_king_armips_ArmipsDroid_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

/**
* void com.king.armips.ArmipsDroid#execute(String[] args)
*/
extern "C" JNIEXPORT void JNICALL
Java_com_king_armips_ArmipsDroid_executeTest(
        JNIEnv* env,
        jobject /* this */,
        jobjectArray jArgs
) {

    // 获取数组长度
    jsize length = env->GetArrayLength(jArgs);

    // 创建 ArmipsArguments 实例
    ArmipsArguments settings;

    jstring jInputFileName = (jstring)env->GetObjectArrayElement(jArgs, 0);
    const char* inputFileNameCStr = env->GetStringUTFChars(jInputFileName, nullptr);
    settings.inputFileName = fs::path(inputFileNameCStr);  // 转换为 fs::path
    env->ReleaseStringUTFChars(jInputFileName, inputFileNameCStr);  // 释放资源

    runArmips(settings);

}

/*
struct ArmipsArguments
{
	// common
	ArmipsMode mode;
	int symFileVersion;
	bool errorOnWarning;
	bool silent;
	bool showStats;
	std::vector<std::string>* errorsResult;
	std::vector<EquationDefinition> equList;
	std::vector<LabelDefinition> labels;

	// file mode
	fs::path inputFileName;
	fs::path tempFileName;
	fs::path symFileName;
	bool useAbsoluteFileNames;

	// memory mode
	std::shared_ptr<AssemblerFile> memoryFile;
	std::string content;

	ArmipsArguments()
	{
		mode = ArmipsMode::FILE;
		symFileVersion = 0;
		errorOnWarning = false;
		silent = false;
		showStats = false;
		errorsResult = nullptr;
		useAbsoluteFileNames = true;
	}
};

public native void execute(int symFileVersion,
                               boolean errorOnWarning,
                               boolean silent,
                               boolean showStats,
                               String[] errorsResult,
                               String[] equNameList,
                               String[] equValueList,
                               String[] labelNameList,
                               long[] labelValueList,
                               String inputFileName,
                               String tempFileName,
                               String symFileName,
                               boolean useAbsoluteFileNames,
                               String rootDir
    );
 */
extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_king_armips_ArmipsDroid_execute(JNIEnv* env,
                                         jobject /* this */,
                                            jint symFileVersion,
                                            jboolean errorOnWarning,
                                            jboolean silent,
                                            jboolean showStats,
                                            jobjectArray equNameList,
                                            jobjectArray equValueList,
                                            jobjectArray labelNameList,
                                            jlongArray labelValueList,
                                            jstring inputFileName,
                                            jstring tempFileName,
                                            jstring symFileName,
                                            jboolean useAbsoluteFileNames,
                                            jstring rootDir
) {

    if (rootDir != nullptr){
        std::error_code errorCode;
        const char * rootDirC = env->GetStringUTFChars(rootDir, nullptr);
        fs::current_path(rootDirC, errorCode);
        env->ReleaseStringUTFChars(rootDir, rootDirC);

        if (errorCode)
        {
            Logger::printError(Logger::Error, "Could not open directory \"%s\"", rootDirC);
//            return env->NewStringUTF(errorMsg.c_str());

            std::string errorMsg = "Could not open directory \"" + std::string(rootDirC) + "\"";

            jobjectArray result = env->NewObjectArray(1, env->FindClass("java/lang/String"), nullptr);
            jstring jstr = env->NewStringUTF(errorMsg.c_str());
            env->SetObjectArrayElement(result, 0, jstr);
            env->DeleteLocalRef(jstr);

            return result;
        }
    }

    // 创建 ArmipsArguments 实例
    ArmipsArguments settings;

    settings.symFileVersion = symFileVersion;
    settings.errorOnWarning = errorOnWarning == JNI_TRUE;
    settings.silent = silent == JNI_TRUE;
    settings.showStats = showStats == JNI_TRUE;

    if (equNameList != nullptr){
        jsize equCount = env->GetArrayLength(equNameList);
        for (int i=0;i<equCount;i++){
            auto nameJ = (jstring)env->GetObjectArrayElement(equNameList, i);
            auto valueJ = (jstring)env->GetObjectArrayElement(equValueList, i);

            const char* nameC = env->GetStringUTFChars(nameJ, nullptr);
            const char* valueC = env->GetStringUTFChars(valueJ, nullptr);

            Identifier name = Identifier(nameC);

            settings.equList.emplace_back(EquationDefinition{name, valueC});

            env->ReleaseStringUTFChars(nameJ, nameC);
            env->ReleaseStringUTFChars(valueJ, valueC);
        }
    }

    if (labelNameList != nullptr){
        jsize labelCount = env->GetArrayLength(labelNameList);
        jlong* labelValues = env->GetLongArrayElements(labelValueList, nullptr);
        for (int i=0;i<labelCount;i++){
            auto nameJ = (jstring)env->GetObjectArrayElement(labelNameList, i);

            const char* nameC = env->GetStringUTFChars(nameJ, nullptr);

            Identifier name = Identifier(nameC);
            jlong value = labelValues[i];

            settings.labels.emplace_back(LabelDefinition{name, value});

            env->ReleaseStringUTFChars(nameJ, nameC);
        }
        env->ReleaseLongArrayElements(labelValueList, labelValues, 0);
    }

    const char* inputFileNameC = env->GetStringUTFChars(inputFileName, nullptr);
    settings.inputFileName = fs::path(inputFileNameC);
    env->ReleaseStringUTFChars(inputFileName, inputFileNameC);

    if (tempFileName != nullptr){
        const char* tempFileNameC = env->GetStringUTFChars(tempFileName, nullptr);
        settings.tempFileName = fs::path(tempFileNameC);
        env->ReleaseStringUTFChars(tempFileName, tempFileNameC);
    }

    if (symFileName != nullptr){
        const char* symFileNameC = env->GetStringUTFChars(symFileName, nullptr);
        settings.symFileName = fs::path(symFileNameC);
        env->ReleaseStringUTFChars(symFileName, symFileNameC);
    }

    settings.useAbsoluteFileNames = useAbsoluteFileNames == JNI_TRUE;

    runArmips(settings);

    std::vector<std::string> errors = Logger::getErrors();
    jsize resultSize = errors.size();
    jobjectArray result = env->NewObjectArray(resultSize, env->FindClass("java/lang/String"), nullptr);
    for (jsize i = 0; i < resultSize; i++) {
        jstring jstr = env->NewStringUTF(errors[i].c_str());
        env->SetObjectArrayElement(result, i, jstr);
        env->DeleteLocalRef(jstr);
    }

    return result;
}

